/**
 * Copyright 2012 Pablo Danilo Aliscioni
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package ar.com.pabloaliscioni.cloud.api;

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import ar.com.pabloaliscioni.cloud.api.exceptions.ConfigurationException;
import ar.com.pabloaliscioni.cloud.core.configuration.Configuration;
import ar.com.pabloaliscioni.cloud.core.configuration.ProviderConfiguration;
import ar.com.pabloaliscioni.cloud.core.configuration.ProviderConfigurationElement;
import ar.com.pabloaliscioni.cloud.core.storage.BaseStorageClient;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

/**
 * Proxy to the cloud service that uses the XML configuration file
 * 
 * @author Pablo Danilo Aliscioni
 * @since 1.0
 */
public class CloudService
{
	/**
	 * Configuration deserialized from the XML configuration file
	 */
	private Configuration configuration;
	
	/**
	 * Collection of storage clients for singleton pattern implementation
	 */
	private Map<String, IStorageClient> storageClients;
	
	/**
	 * Parameterized constructor
	 * 
	 * @param configuration Configuration deserialized from the XML configuration file
	 */
	private CloudService(Configuration configuration)
	{
		super();
		
		this.configuration = configuration;
		this.storageClients = new HashMap<String, IStorageClient>();
	}
	
	/**
	 * Gets an instance of the cloud service
	 * 
	 * @return An instance of the cloud service
	 */
	public static CloudService buildInstance(Configuration configuration)
	{
		return new CloudService(configuration);
	}
	
	/**
	 * Gets an instance of the cloud service
	 * 
	 * @return An instance of the cloud service
	 */
	public static CloudService buildInstance(String configurationFile)
	{
		return new CloudService(buildConfiguration(configurationFile));
	}
	
	/**
	 * Gets an instance of the cloud service
	 * 
	 * @return An instance of the cloud service
	 */
	public static CloudService buildInstance()
	{
		return new CloudService(buildConfiguration("UnifiedCloudAPI-configuration.xml"));
	}
	
	/**
	 * Gets an instance of the default provider
	 * 
	 * @return An instance of the default provider
	 */
	public IStorageClient getProvider()
	{
		return getProvider(configuration.getDefaultProviderName());
	}
	
	/**
	 * Gets an instance of the given provider
	 * 
	 * @param providerName The name of the provider to return
	 * 
	 * @return An instance of the given provider
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public IStorageClient getProvider(String providerName)
	{
		if(!storageClients.containsKey(providerName))
		{
			ProviderConfiguration providerConfiguration = configuration.getProviderConfiguration(providerName);
			
			try
			{
				Class c = Class.forName(providerConfiguration.getImplementation());
				Constructor<BaseStorageClient> ct = c.getConstructor();
				
				BaseStorageClient client = ct.newInstance();
				client.configure(providerConfiguration);
				
				storageClients.put(providerName, client);
			}
			catch (Exception e)
			{
				throw new ConfigurationException(e);
			}
		}
		
		return storageClients.get(providerName);
	}
	
	/**
	 * Gets a list containing all available providers.
	 * 
	 * @return A list of all available providers
	 */
	public Set<String> getProviders()
	{
		Set<String> providers = new HashSet<String>();
		
		for (ProviderConfiguration providerConfiguration : configuration.getProviderConfigurations())
		{
			providers.add(providerConfiguration.getName());
		}
		
		return providers;
	}
	
	public ProviderConfiguration getProviderConfiguration(String providerName)
	{
		if(providerName == null || providerName.isEmpty())
			throw new IllegalArgumentException("The provider name is invalid");
		
		for (ProviderConfiguration providerConfiguration : configuration.getProviderConfigurations())
		{
			if(providerConfiguration.getName().equals(providerName))
			{
				return providerConfiguration;
			}
		}
		
		throw new IllegalArgumentException("Invalid provider name");
	}
	
	/**
	 * Builds a configuration deserializing the XML configuration file
	 * 
	 * @return A configuration deserialized from the XML configuration file
	 */
	private static Configuration buildConfiguration(String configurationFile)
	{
		InputStream inputStream = ClassLoader.getSystemResourceAsStream(configurationFile);
		
		Configuration configuration = (Configuration)buildXMLParser().fromXML(inputStream);
		
		return configuration;
	}
	
	/**
	 * Builds an XML parser for deserializing the XML configuration file
	 * 
	 * @return An XML parser for deserializing the XML configuration file
	 */
	private static XStream buildXMLParser()
	{
		XStream xstream = new XStream(new DomDriver());
		
		xstream.alias("configuration", Configuration.class);
		xstream.alias("provider", ProviderConfiguration.class);
		xstream.alias("configurationItem", ProviderConfigurationElement.class);
		
		xstream.autodetectAnnotations(true);
		
		return xstream;
	}
}
